library(nycflights13)
library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
package ‘ggplot2’ was built under R version 3.3.2Want to understand how all the pieces fit together?
Buy the ggplot2 book: http://ggplot2.org/book/
Conflicts with tidy packages ---------------------------------
filter(): dplyr, stats
lag():    dplyr, stats

5.2 Filter rows with filter()

5.2.4 Exercises

  1. Find all flights that:
  1. Had an arrival delay of two or more hours
filter(flights, arr_delay >= 120)
  1. Flew to Houston (IAH or HOU)
filter(flights, dest %in% c("IAH", "HOU"))
  1. Were operated by United, American, or Delta
airlines
filter(flights, carrier %in% c("UA", "AA", "DL"))
  1. Departed in summer (July, August, and September)
filter(flights, month %in% c(7, 8, 9))
  1. Arrived more than two hours late, but didn’t leave late
filter(flights, arr_delay > 120, dep_delay <= 0)
  1. Were delayed by at least an hour, but made up over 30 minutes in flight
filter(flights, arr_delay >= 60, arr_delay - dep_delay < -30)
  1. Departed between midnight and 6am (inclusive)
filter(flights, dep_time <= 600 | dep_time == 2400)
  1. Another useful dplyr filtering helper is between(). What does it do? Can you use it to simplify the code needed to answer the previous challenges?

between() returns items that have a variable value between two boundary values (inclusive, ie it tests for >= and <= on the left and right boundaries).

  1. Departed in summer (July, August, and September)
filter(flights, between(month, 7, 9))
  1. How many flights have a missing dep_time? What other variables are missing? What might these rows represent?
filter(flights, is.na(dep_time))

These rows are also missing dep_delay, arr_time, arr_delay and air_time. Given that they have all the scheduled details but are missing all actual flight data, these rows appear to represent cancelled flights.

  1. Why is NA ^ 0 not missing? Why is NA | TRUE not missing? Why is FALSE & NA not missing? Can you figure out the general rule? (NA * 0 is a tricky counterexample!)
NA ^ 0
[1] 1
NA | TRUE
[1] TRUE
NA | FALSE # Counter-example
[1] NA
FALSE & NA
[1] FALSE
TRUE & NA # Counter-example
[1] NA
NA * 0
[1] NA

NA ^ 0 is not missing because any value to the power of zero equals 1 (although I don’t have an explanation right now why the same principle doesn’t apply for NA * 0). NA | TRUE is not missing because only one side of the ‘or’ operator needs to evaluate as true (conversely, note that NA | FALSE is missing). FALSE & NA is not missing because both sides of the ‘and’ operator would need to evaluate TRUE for it to be true so based on the right-hand side it would be false regardless of the actual value of the NA (conversely, note that TRUE & NA is missing).

5.3 Arrange rows with arrange()

5.3.1 Exercises

  1. How could you use arrange() to sort all missing values to the start? (Hint: use is.na()).

Using dep_time as an example.

arrange(flights, desc(is.na(dep_time)))
  1. Sort flights to find the most delayed flights. Find the flights that left earliest.
arrange(flights, desc(arr_delay), dep_delay)
  1. Sort flights to find the fastest flights.
arrange(flights, air_time)
  1. Which flights travelled the longest? Which travelled the shortest?
arrange(flights, desc(distance))
arrange(flights, distance)

5.4 Select columns with select()

5.4.1 Exercises

  1. Brainstorm as many ways as possible to select dep_time, dep_delay, arr_time, and arr_delay from flights.
select(flights, dep_time, dep_delay, arr_time, arr_delay)
select(flights, 4, 6, 7, 9)
select(flights, starts_with("dep_"), starts_with("arr_"))

Could keep going with a minus operator to drop all of the other columns, etc.

  1. What happens if you include the name of a variable multiple times in a select() call?
select(flights, dep_time, dep_time)

It doesn’t duplicate the variable.

  1. What does the one_of() function do? Why might it be helpful in conjunction with this vector?

It allows selection of variables by matching against a vector of strongs. In the code below I’ve used it to select all of the variables that aren’t listed in the vector.

vars <- c("year", "month", "day", "dep_delay", "arr_delay")
select(flights, -one_of(vars))
  1. Does the result of running the following code surprise you? How do the select helpers deal with case by default? How can you change that default?
select(flights, contains("TIME"))

By default the select helpers are case-insensitive. It can be modified by passing the argument ignore.case = FALSE.

select(flights, contains("TIME", ignore.case = FALSE))

5.5 Add new variables with mutate()

5.5.2 Exercises

  1. Currently dep_time and sched_dep_time are convenient to look at, but hard to compute with because they’re not really continuous numbers. Convert them to a more convenient representation of number of minutes since midnight.
mutate(
  flights,
  dep_time_min = ((dep_time %/% 100) * 60) + (dep_time %% 100),
  sched_dep_time_min = ((sched_dep_time %/% 100) * 60) + (sched_dep_time %% 100)
)
  1. Compare air_time with arr_time - dep_time. What do you expect to see? What do you see? What do you need to do to fix it?

I would expect that air_time = arr_time - dep_time.

mutate(
  flights,
  diff_time = arr_time - dep_time
)

This isn’t correct. The first problem is the issue with the way hours-and-minutes expressions of the time are entered into a single variable, which can be addressed as in the previous exercise.

mutate(
  flights,
  dep_time_min = ((dep_time %/% 100) * 60) + (dep_time %% 100),
  arr_time_min = ((arr_time %/% 100) * 60) + (arr_time %% 100),
  diff_time = arr_time_min - dep_time_min
)

Most of the times still don’t match up. The documentation for the dataset indicates that the arrival and departure times are based on local timezones, so that is one clear remaining complication that couldn’t be resolved without performing timezone conversions based on origin and destination, but I don’t think that is the full explanation of why there isn’t a match. I’m setting this one aside at this point and might try to come back to it.

  1. Compare dep_time, sched_dep_time, and dep_delay. How would you expect those three numbers to be related?

I would expect that dep_delay = dep_time - sched_dep_time. Let’s test it out.

mutate(
  flights,
  dep_diff = dep_time - sched_dep_time
)

This works in some cases but not universally, again because the times are expressed as a 24-hour time value but in a single variable rather than separating hours and days. Transforming those times into minutes since midnight should fix that.

mutate(
  flights,
  dep_time_min = ((dep_time %/% 100) * 60) + (dep_time %% 100),
  sched_dep_time_min = ((sched_dep_time %/% 100) * 60) + (sched_dep_time %% 100),
  dep_diff = dep_time_min - sched_dep_time_min
)

That appears to work for most cases, except it fails where there was a delay that saw a flight delayed past midnight and into the next day.

  1. Find the 10 most delayed flights using a ranking function. How do you want to handle ties? Carefully read the documentation for min_rank().
mutate(
  flights,
  delay_rank = min_rank(desc(dep_delay))
) %>%
  arrange(delay_rank)

It makes sense for ties to be ranked equally, e.g., three flights are tied for the 12th longest delay, while the next item takes into account the number of items ranked ahead of it rather than just the number of values, e.g., the next ranked item is 15th because 14 flights were delayed longer than it. This is the default behaviour of min_rank which the help describes as equivalent to rank(ties.method = "min").

  1. What does 1:3 + 1:10 return? Why?
1:3 + 1:10
longer object length is not a multiple of shorter object length
 [1]  2  4  6  5  7  9  8 10 12 11

The addition operator is vectorised with recycling, so the first (3-item) vector repeats itself through until it matches the second (10-item) vector, i.e., it ends up being equivalent to c(1, 2, 3, 1, 2, 3, 1, 2, 3, 1) + c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).

  1. What trigonometric functions does R provide?

See ?Trig for info on the package with the main trigonometry functions: cos(), sin(), tan(), acos(), asin(), atan(), atan2(), cospi(), sinpi() and tanpi().

5.6 Grouped summaries with summarise()

5.6.7 Exercises

  1. Brainstorm at least 5 different ways to assess the typical delay characteristics of a group of flights. Consider the following scenarios:
  • A flight is 15 minutes early 50% of the time, and 15 minutes late 50% of the time.

  • A flight is always 10 minutes late.

  • A flight is 30 minutes early 50% of the time, and 30 minutes late 50% of the time.

  • 99% of the time a flight is on time. 1% of the time it’s 2 hours late.

Which is more important: arrival delay or departure delay?

  1. Come up with another approach that will give you the same output as not_cancelled %>% count(dest) and not_cancelled %>% count(tailnum, wt = distance) (without using count()).
not_cancelled <- flights %>% 
  filter(!is.na(dep_delay), !is.na(arr_delay))
# Equivalent to not_cancelled %>% count(dest):
not_cancelled %>%
  group_by(dest) %>%
  summarise(n())
# Equivalent to not_cancelled %>% count(tailnum, wt = distance):
not_cancelled %>%
  group_by(tailnum) %>%
  summarise(n = sum(distance))
  1. Our definition of cancelled flights (is.na(dep_delay) | is.na(arr_delay) ) is slightly suboptimal. Why? Which is the most important column?

  2. Look at the number of cancelled flights per day. Is there a pattern? Is the proportion of cancelled flights related to the average delay?

flights %>%
  group_by(year, month, day) %>%
  summarise(
    n_cancelled = sum(is.na(dep_time))
  ) %>%
  ggplot(mapping = aes(x = n_cancelled)) +
  geom_bar()

Most days have a relatively small number of flights cancelled and there are some infrequent days where many flights are cancalled.

flights %>%
  group_by(year, month, day) %>%
  summarise(
    prop_cancelled = mean(is.na(dep_time)),
    mean_delay = mean(dep_delay, na.rm = TRUE)
  ) %>%
  ggplot(mapping = aes(x = mean_delay, y = prop_cancelled)) +
  geom_point() +
  geom_smooth()

There are some unusual days but the general pattern is that days with longer delays also tend to have a greater proportion of flights cancelled.

  1. Which carrier has the worst delays? Challenge: can you disentangle the effects of bad airports vs. bad carriers? Why/why not? (Hint: think about flights %>% group_by(carrier, dest) %>% summarise(n()))
airline_delays <- flights %>%
  group_by(carrier) %>%
  summarise(
    mean_delay = mean(arr_delay, na.rm = TRUE),
    n_flights = n()
  ) %>%
  arrange(desc(mean_delay))
airline_delays

Frontier (F9) and AirTran (FL) are the two with the highest mean arrival delays but note that they have a relatively low number of flights (in the hundreds and thousands, respectively). Of airlines that have tens of thousands of flights, ExpressJet (EV) has mean delays 5 minutes longer than other airlines. (Note that carrier codes can be matched to airline names in the airlines data file.)

ggplot(data = airline_delays) +
  geom_point(mapping = aes(x = n_flights, y = mean_delay))

airline_dest_delays <- flights %>%
  group_by(carrier, dest) %>%
  summarise(
    mean_delay = mean(arr_delay, na.rm = TRUE),
    n_flights = n()
  ) %>%
  arrange(desc(mean_delay))
airline_dest_delays

In attempting to disentangle carrier vs airport issues, some of the combinations of carrier and airport have a very small number of flights and should be interpreted with caution.

ggplot(data = airline_dest_delays) +
  geom_point(mapping = aes(x = n_flights, y = mean_delay), alpha = 2/10)

I might come back and explore the data some more to see what conclusions can be drawn with the volatile small subsamples removed.

  1. For each plane, count the number of flights before the first delay of greater than 1 hour.

  2. What does the sort argument to count() do. When might you use it?

If sort = TRUE then the count() function arranges its output in descending order. I would use it when you want to find the most frequent values, as it saves piping the count() results into an arrange() call.

not_cancelled %>% count(dest, sort = TRUE)
LS0tCnRpdGxlOiAiQ2hhcHRlciA1OiBEYXRhIFRyYW5zZm9ybWF0aW9uIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeShueWNmbGlnaHRzMTMpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMgNS4yIEZpbHRlciByb3dzIHdpdGggYGZpbHRlcigpYAoKIyMgNS4yLjQgRXhlcmNpc2VzCgoxLiBGaW5kIGFsbCBmbGlnaHRzIHRoYXQ6CgphLiBIYWQgYW4gYXJyaXZhbCBkZWxheSBvZiB0d28gb3IgbW9yZSBob3VycwoKYGBge3J9CmZpbHRlcihmbGlnaHRzLCBhcnJfZGVsYXkgPj0gMTIwKQpgYGAKCmIuIEZsZXcgdG8gSG91c3RvbiAoSUFIIG9yIEhPVSkKCmBgYHtyfQpmaWx0ZXIoZmxpZ2h0cywgZGVzdCAlaW4lIGMoIklBSCIsICJIT1UiKSkKYGBgCgpjLiBXZXJlIG9wZXJhdGVkIGJ5IFVuaXRlZCwgQW1lcmljYW4sIG9yIERlbHRhCgpgYGB7cn0KYWlybGluZXMKZmlsdGVyKGZsaWdodHMsIGNhcnJpZXIgJWluJSBjKCJVQSIsICJBQSIsICJETCIpKQpgYGAKCmQuIERlcGFydGVkIGluIHN1bW1lciAoSnVseSwgQXVndXN0LCBhbmQgU2VwdGVtYmVyKQoKYGBge3J9CmZpbHRlcihmbGlnaHRzLCBtb250aCAlaW4lIGMoNywgOCwgOSkpCmBgYAoKZS4gQXJyaXZlZCBtb3JlIHRoYW4gdHdvIGhvdXJzIGxhdGUsIGJ1dCBkaWRu4oCZdCBsZWF2ZSBsYXRlCgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIGFycl9kZWxheSA+IDEyMCwgZGVwX2RlbGF5IDw9IDApCmBgYAoKZi4gV2VyZSBkZWxheWVkIGJ5IGF0IGxlYXN0IGFuIGhvdXIsIGJ1dCBtYWRlIHVwIG92ZXIgMzAgbWludXRlcyBpbiBmbGlnaHQKCmBgYHtyfQpmaWx0ZXIoZmxpZ2h0cywgYXJyX2RlbGF5ID49IDYwLCBhcnJfZGVsYXkgLSBkZXBfZGVsYXkgPCAtMzApCmBgYAoKZy4gRGVwYXJ0ZWQgYmV0d2VlbiBtaWRuaWdodCBhbmQgNmFtIChpbmNsdXNpdmUpCgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIGRlcF90aW1lIDw9IDYwMCB8IGRlcF90aW1lID09IDI0MDApCmBgYAoKMi4gQW5vdGhlciB1c2VmdWwgZHBseXIgZmlsdGVyaW5nIGhlbHBlciBpcyBgYmV0d2VlbigpYC4gV2hhdCBkb2VzIGl0IGRvPyBDYW4geW91IHVzZSBpdCB0byBzaW1wbGlmeSB0aGUgY29kZSBuZWVkZWQgdG8gYW5zd2VyIHRoZSBwcmV2aW91cyBjaGFsbGVuZ2VzPwoKKipgYmV0d2VlbigpYCByZXR1cm5zIGl0ZW1zIHRoYXQgaGF2ZSBhIHZhcmlhYmxlIHZhbHVlIGJldHdlZW4gdHdvIGJvdW5kYXJ5IHZhbHVlcyAoaW5jbHVzaXZlLCBpZSBpdCB0ZXN0cyBmb3IgYD49YCBhbmQgYDw9YCBvbiB0aGUgbGVmdCBhbmQgcmlnaHQgYm91bmRhcmllcykuKioKCmQuIERlcGFydGVkIGluIHN1bW1lciAoSnVseSwgQXVndXN0LCBhbmQgU2VwdGVtYmVyKQoKYGBge3J9CmZpbHRlcihmbGlnaHRzLCBiZXR3ZWVuKG1vbnRoLCA3LCA5KSkKYGBgCgozLiBIb3cgbWFueSBmbGlnaHRzIGhhdmUgYSBtaXNzaW5nIGBkZXBfdGltZWA/IFdoYXQgb3RoZXIgdmFyaWFibGVzIGFyZSBtaXNzaW5nPyBXaGF0IG1pZ2h0IHRoZXNlIHJvd3MgcmVwcmVzZW50PwoKYGBge3J9CmZpbHRlcihmbGlnaHRzLCBpcy5uYShkZXBfdGltZSkpCmBgYAoKKipUaGVzZSByb3dzIGFyZSBhbHNvIG1pc3NpbmcgYGRlcF9kZWxheWAsIGBhcnJfdGltZWAsIGBhcnJfZGVsYXlgIGFuZCBgYWlyX3RpbWVgLiBHaXZlbiB0aGF0IHRoZXkgaGF2ZSBhbGwgdGhlIHNjaGVkdWxlZCBkZXRhaWxzIGJ1dCBhcmUgbWlzc2luZyBhbGwgYWN0dWFsIGZsaWdodCBkYXRhLCB0aGVzZSByb3dzIGFwcGVhciB0byByZXByZXNlbnQgY2FuY2VsbGVkIGZsaWdodHMuKioKCjQuIFdoeSBpcyBgTkEgXiAwYCBub3QgbWlzc2luZz8gV2h5IGlzIGBOQSB8IFRSVUVgIG5vdCBtaXNzaW5nPyBXaHkgaXMgYEZBTFNFICYgTkFgIG5vdCBtaXNzaW5nPyBDYW4geW91IGZpZ3VyZSBvdXQgdGhlIGdlbmVyYWwgcnVsZT8gKGBOQSAqIDBgIGlzIGEgdHJpY2t5IGNvdW50ZXJleGFtcGxlISkKCmBgYHtyfQpOQSBeIDAKTkEgfCBUUlVFCk5BIHwgRkFMU0UgIyBDb3VudGVyLWV4YW1wbGUKRkFMU0UgJiBOQQpUUlVFICYgTkEgIyBDb3VudGVyLWV4YW1wbGUKTkEgKiAwCmBgYAoKKipgTkEgXiAwYCBpcyBub3QgbWlzc2luZyBiZWNhdXNlIGFueSB2YWx1ZSB0byB0aGUgcG93ZXIgb2YgemVybyBlcXVhbHMgMSAoYWx0aG91Z2ggSSBkb24ndCBoYXZlIGFuIGV4cGxhbmF0aW9uIHJpZ2h0IG5vdyB3aHkgdGhlIHNhbWUgcHJpbmNpcGxlIGRvZXNuJ3QgYXBwbHkgZm9yIGBOQSAqIDBgKS4gYE5BIHwgVFJVRWAgaXMgbm90IG1pc3NpbmcgYmVjYXVzZSBvbmx5IG9uZSBzaWRlIG9mIHRoZSAnb3InIG9wZXJhdG9yIG5lZWRzIHRvIGV2YWx1YXRlIGFzIHRydWUgKGNvbnZlcnNlbHksIG5vdGUgdGhhdCBgTkEgfCBGQUxTRWAgaXMgbWlzc2luZykuIGBGQUxTRSAmIE5BYCBpcyBub3QgbWlzc2luZyBiZWNhdXNlIGJvdGggc2lkZXMgb2YgdGhlICdhbmQnIG9wZXJhdG9yIHdvdWxkIG5lZWQgdG8gZXZhbHVhdGUgVFJVRSBmb3IgaXQgdG8gYmUgdHJ1ZSBzbyBiYXNlZCBvbiB0aGUgcmlnaHQtaGFuZCBzaWRlIGl0IHdvdWxkIGJlIGZhbHNlIHJlZ2FyZGxlc3Mgb2YgdGhlIGFjdHVhbCB2YWx1ZSBvZiB0aGUgYE5BYCAoY29udmVyc2VseSwgbm90ZSB0aGF0IGBUUlVFICYgTkFgIGlzIG1pc3NpbmcpLioqCgojIDUuMyBBcnJhbmdlIHJvd3Mgd2l0aCBgYXJyYW5nZSgpYAoKIyMgNS4zLjEgRXhlcmNpc2VzCgoxLiBIb3cgY291bGQgeW91IHVzZSBgYXJyYW5nZSgpYCB0byBzb3J0IGFsbCBtaXNzaW5nIHZhbHVlcyB0byB0aGUgc3RhcnQ/IChIaW50OiB1c2UgYGlzLm5hKClgKS4KCioqVXNpbmcgYGRlcF90aW1lYCBhcyBhbiBleGFtcGxlLioqCgpgYGB7cn0KYXJyYW5nZShmbGlnaHRzLCBkZXNjKGlzLm5hKGRlcF90aW1lKSkpCmBgYAoKMi4gU29ydCBmbGlnaHRzIHRvIGZpbmQgdGhlIG1vc3QgZGVsYXllZCBmbGlnaHRzLiBGaW5kIHRoZSBmbGlnaHRzIHRoYXQgbGVmdCBlYXJsaWVzdC4KCmBgYHtyfQphcnJhbmdlKGZsaWdodHMsIGRlc2MoYXJyX2RlbGF5KSwgZGVwX2RlbGF5KQpgYGAKCjMuIFNvcnQgZmxpZ2h0cyB0byBmaW5kIHRoZSBmYXN0ZXN0IGZsaWdodHMuCgpgYGB7cn0KYXJyYW5nZShmbGlnaHRzLCBhaXJfdGltZSkKYGBgCgo0LiBXaGljaCBmbGlnaHRzIHRyYXZlbGxlZCB0aGUgbG9uZ2VzdD8gV2hpY2ggdHJhdmVsbGVkIHRoZSBzaG9ydGVzdD8KCmBgYHtyfQphcnJhbmdlKGZsaWdodHMsIGRlc2MoZGlzdGFuY2UpKQpgYGAKCmBgYHtyfQphcnJhbmdlKGZsaWdodHMsIGRpc3RhbmNlKQpgYGAKCiMgNS40IFNlbGVjdCBjb2x1bW5zIHdpdGggYHNlbGVjdCgpYAoKIyMgNS40LjEgRXhlcmNpc2VzCgoxLiBCcmFpbnN0b3JtIGFzIG1hbnkgd2F5cyBhcyBwb3NzaWJsZSB0byBzZWxlY3QgYGRlcF90aW1lYCwgYGRlcF9kZWxheWAsIGBhcnJfdGltZWAsIGFuZCBgYXJyX2RlbGF5YCBmcm9tIGZsaWdodHMuCgpgYGB7cn0Kc2VsZWN0KGZsaWdodHMsIGRlcF90aW1lLCBkZXBfZGVsYXksIGFycl90aW1lLCBhcnJfZGVsYXkpCmBgYAoKYGBge3J9CnNlbGVjdChmbGlnaHRzLCA0LCA2LCA3LCA5KQpgYGAKCmBgYHtyfQpzZWxlY3QoZmxpZ2h0cywgc3RhcnRzX3dpdGgoImRlcF8iKSwgc3RhcnRzX3dpdGgoImFycl8iKSkKYGBgCgoqKkNvdWxkIGtlZXAgZ29pbmcgd2l0aCBhIG1pbnVzIG9wZXJhdG9yIHRvIGRyb3AgYWxsIG9mIHRoZSBvdGhlciBjb2x1bW5zLCBldGMuKioKCjIuIFdoYXQgaGFwcGVucyBpZiB5b3UgaW5jbHVkZSB0aGUgbmFtZSBvZiBhIHZhcmlhYmxlIG11bHRpcGxlIHRpbWVzIGluIGEgYHNlbGVjdCgpYCBjYWxsPwoKYGBge3J9CnNlbGVjdChmbGlnaHRzLCBkZXBfdGltZSwgZGVwX3RpbWUpCmBgYAoKKipJdCBkb2Vzbid0IGR1cGxpY2F0ZSB0aGUgdmFyaWFibGUuKioKCjMuIFdoYXQgZG9lcyB0aGUgYG9uZV9vZigpYCBmdW5jdGlvbiBkbz8gV2h5IG1pZ2h0IGl0IGJlIGhlbHBmdWwgaW4gY29uanVuY3Rpb24gd2l0aCB0aGlzIHZlY3Rvcj8KCioqSXQgYWxsb3dzIHNlbGVjdGlvbiBvZiB2YXJpYWJsZXMgYnkgbWF0Y2hpbmcgYWdhaW5zdCBhIHZlY3RvciBvZiBzdHJvbmdzLiBJbiB0aGUgY29kZSBiZWxvdyBJJ3ZlIHVzZWQgaXQgdG8gc2VsZWN0IGFsbCBvZiB0aGUgdmFyaWFibGVzIHRoYXQgX2FyZW4ndF8gbGlzdGVkIGluIHRoZSB2ZWN0b3IuKioKCmBgYHtyfQp2YXJzIDwtIGMoInllYXIiLCAibW9udGgiLCAiZGF5IiwgImRlcF9kZWxheSIsICJhcnJfZGVsYXkiKQpzZWxlY3QoZmxpZ2h0cywgLW9uZV9vZih2YXJzKSkKYGBgCgo0LiBEb2VzIHRoZSByZXN1bHQgb2YgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvZGUgc3VycHJpc2UgeW91PyBIb3cgZG8gdGhlIGBzZWxlY3RgIGhlbHBlcnMgZGVhbCB3aXRoIGNhc2UgYnkgZGVmYXVsdD8gSG93IGNhbiB5b3UgY2hhbmdlIHRoYXQgZGVmYXVsdD8KCmBgYHtyfQpzZWxlY3QoZmxpZ2h0cywgY29udGFpbnMoIlRJTUUiKSkKYGBgCgoqKkJ5IGRlZmF1bHQgdGhlIGBzZWxlY3RgIGhlbHBlcnMgYXJlIGNhc2UtaW5zZW5zaXRpdmUuIEl0IGNhbiBiZSBtb2RpZmllZCBieSBwYXNzaW5nIHRoZSBhcmd1bWVudCBgaWdub3JlLmNhc2UgPSBGQUxTRWAuKioKCmBgYHtyfQpzZWxlY3QoZmxpZ2h0cywgY29udGFpbnMoIlRJTUUiLCBpZ25vcmUuY2FzZSA9IEZBTFNFKSkKYGBgCgojIDUuNSBBZGQgbmV3IHZhcmlhYmxlcyB3aXRoIGBtdXRhdGUoKWAKCiMjIDUuNS4yIEV4ZXJjaXNlcwoKMS4gQ3VycmVudGx5IGBkZXBfdGltZWAgYW5kIGBzY2hlZF9kZXBfdGltZWAgYXJlIGNvbnZlbmllbnQgdG8gbG9vayBhdCwgYnV0IGhhcmQgdG8gY29tcHV0ZSB3aXRoIGJlY2F1c2UgdGhleeKAmXJlIG5vdCByZWFsbHkgY29udGludW91cyBudW1iZXJzLiBDb252ZXJ0IHRoZW0gdG8gYSBtb3JlIGNvbnZlbmllbnQgcmVwcmVzZW50YXRpb24gb2YgbnVtYmVyIG9mIG1pbnV0ZXMgc2luY2UgbWlkbmlnaHQuCgpgYGB7cn0KbXV0YXRlKAogIGZsaWdodHMsCiAgZGVwX3RpbWVfbWluID0gKChkZXBfdGltZSAlLyUgMTAwKSAqIDYwKSArIChkZXBfdGltZSAlJSAxMDApLAogIHNjaGVkX2RlcF90aW1lX21pbiA9ICgoc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCkgKiA2MCkgKyAoc2NoZWRfZGVwX3RpbWUgJSUgMTAwKQopCmBgYAoKMi4gQ29tcGFyZSBgYWlyX3RpbWVgIHdpdGggYGFycl90aW1lIC0gZGVwX3RpbWVgLiBXaGF0IGRvIHlvdSBleHBlY3QgdG8gc2VlPyBXaGF0IGRvIHlvdSBzZWU/IFdoYXQgZG8geW91IG5lZWQgdG8gZG8gdG8gZml4IGl0PwoKKipJIHdvdWxkIGV4cGVjdCB0aGF0IGBhaXJfdGltZSA9IGFycl90aW1lIC0gZGVwX3RpbWVgLioqCgpgYGB7cn0KbXV0YXRlKAogIGZsaWdodHMsCiAgZGlmZl90aW1lID0gYXJyX3RpbWUgLSBkZXBfdGltZQopCmBgYAoKKipUaGlzIGlzbid0IGNvcnJlY3QuIFRoZSBmaXJzdCBwcm9ibGVtIGlzIHRoZSBpc3N1ZSB3aXRoIHRoZSB3YXkgaG91cnMtYW5kLW1pbnV0ZXMgZXhwcmVzc2lvbnMgb2YgdGhlIHRpbWUgYXJlIGVudGVyZWQgaW50byBhIHNpbmdsZSB2YXJpYWJsZSwgd2hpY2ggY2FuIGJlIGFkZHJlc3NlZCBhcyBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UuKioKCmBgYHtyfQptdXRhdGUoCiAgZmxpZ2h0cywKICBkZXBfdGltZV9taW4gPSAoKGRlcF90aW1lICUvJSAxMDApICogNjApICsgKGRlcF90aW1lICUlIDEwMCksCiAgYXJyX3RpbWVfbWluID0gKChhcnJfdGltZSAlLyUgMTAwKSAqIDYwKSArIChhcnJfdGltZSAlJSAxMDApLAogIGRpZmZfdGltZSA9IGFycl90aW1lX21pbiAtIGRlcF90aW1lX21pbgopCmBgYAoKKipNb3N0IG9mIHRoZSB0aW1lcyBzdGlsbCBkb24ndCBtYXRjaCB1cC4gVGhlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBkYXRhc2V0IGluZGljYXRlcyB0aGF0IHRoZSBhcnJpdmFsIGFuZCBkZXBhcnR1cmUgdGltZXMgYXJlIGJhc2VkIG9uIGxvY2FsIHRpbWV6b25lcywgc28gdGhhdCBpcyBvbmUgY2xlYXIgcmVtYWluaW5nIGNvbXBsaWNhdGlvbiB0aGF0IGNvdWxkbid0IGJlIHJlc29sdmVkIHdpdGhvdXQgcGVyZm9ybWluZyB0aW1lem9uZSBjb252ZXJzaW9ucyBiYXNlZCBvbiBvcmlnaW4gYW5kIGRlc3RpbmF0aW9uLCBidXQgSSBkb24ndCB0aGluayB0aGF0IGlzIHRoZSBmdWxsIGV4cGxhbmF0aW9uIG9mIHdoeSB0aGVyZSBpc24ndCBhIG1hdGNoLiBJJ20gc2V0dGluZyB0aGlzIG9uZSBhc2lkZSBhdCB0aGlzIHBvaW50IGFuZCBtaWdodCB0cnkgdG8gY29tZSBiYWNrIHRvIGl0LioqCgozLiBDb21wYXJlIGBkZXBfdGltZWAsIGBzY2hlZF9kZXBfdGltZWAsIGFuZCBgZGVwX2RlbGF5YC4gSG93IHdvdWxkIHlvdSBleHBlY3QgdGhvc2UgdGhyZWUgbnVtYmVycyB0byBiZSByZWxhdGVkPwoKKipJIHdvdWxkIGV4cGVjdCB0aGF0IGBkZXBfZGVsYXkgPSBkZXBfdGltZSAtIHNjaGVkX2RlcF90aW1lYC4gTGV0J3MgdGVzdCBpdCBvdXQuKioKCmBgYHtyfQptdXRhdGUoCiAgZmxpZ2h0cywKICBkZXBfZGlmZiA9IGRlcF90aW1lIC0gc2NoZWRfZGVwX3RpbWUKKQpgYGAKCioqVGhpcyB3b3JrcyBpbiBzb21lIGNhc2VzIGJ1dCBub3QgdW5pdmVyc2FsbHksIGFnYWluIGJlY2F1c2UgdGhlIHRpbWVzIGFyZSBleHByZXNzZWQgYXMgYSAyNC1ob3VyIHRpbWUgdmFsdWUgYnV0IGluIGEgc2luZ2xlIHZhcmlhYmxlIHJhdGhlciB0aGFuIHNlcGFyYXRpbmcgaG91cnMgYW5kIGRheXMuIFRyYW5zZm9ybWluZyB0aG9zZSB0aW1lcyBpbnRvIG1pbnV0ZXMgc2luY2UgbWlkbmlnaHQgc2hvdWxkIGZpeCB0aGF0LioqCgpgYGB7cn0KbXV0YXRlKAogIGZsaWdodHMsCiAgZGVwX3RpbWVfbWluID0gKChkZXBfdGltZSAlLyUgMTAwKSAqIDYwKSArIChkZXBfdGltZSAlJSAxMDApLAogIHNjaGVkX2RlcF90aW1lX21pbiA9ICgoc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCkgKiA2MCkgKyAoc2NoZWRfZGVwX3RpbWUgJSUgMTAwKSwKICBkZXBfZGlmZiA9IGRlcF90aW1lX21pbiAtIHNjaGVkX2RlcF90aW1lX21pbgopCmBgYAoKKipUaGF0IGFwcGVhcnMgdG8gd29yayBmb3IgbW9zdCBjYXNlcywgZXhjZXB0IGl0IGZhaWxzIHdoZXJlIHRoZXJlIHdhcyBhIGRlbGF5IHRoYXQgc2F3IGEgZmxpZ2h0IGRlbGF5ZWQgcGFzdCBtaWRuaWdodCBhbmQgaW50byB0aGUgbmV4dCBkYXkuKioKCjQuIEZpbmQgdGhlIDEwIG1vc3QgZGVsYXllZCBmbGlnaHRzIHVzaW5nIGEgcmFua2luZyBmdW5jdGlvbi4gSG93IGRvIHlvdSB3YW50IHRvIGhhbmRsZSB0aWVzPyBDYXJlZnVsbHkgcmVhZCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgYG1pbl9yYW5rKClgLgoKYGBge3J9Cm11dGF0ZSgKICBmbGlnaHRzLAogIGRlbGF5X3JhbmsgPSBtaW5fcmFuayhkZXNjKGRlcF9kZWxheSkpCikgJT4lCiAgYXJyYW5nZShkZWxheV9yYW5rKQpgYGAKCioqSXQgbWFrZXMgc2Vuc2UgZm9yIHRpZXMgdG8gYmUgcmFua2VkIGVxdWFsbHksIGUuZy4sIHRocmVlIGZsaWdodHMgYXJlIHRpZWQgZm9yIHRoZSAxMnRoIGxvbmdlc3QgZGVsYXksIHdoaWxlIHRoZSBuZXh0IGl0ZW0gdGFrZXMgaW50byBhY2NvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgcmFua2VkIGFoZWFkIG9mIGl0IHJhdGhlciB0aGFuIGp1c3QgdGhlIG51bWJlciBvZiB2YWx1ZXMsIGUuZy4sIHRoZSBuZXh0IHJhbmtlZCBpdGVtIGlzIDE1dGggYmVjYXVzZSAxNCBmbGlnaHRzIHdlcmUgZGVsYXllZCBsb25nZXIgdGhhbiBpdC4gVGhpcyBpcyB0aGUgZGVmYXVsdCBiZWhhdmlvdXIgb2YgYG1pbl9yYW5rYCB3aGljaCB0aGUgaGVscCBkZXNjcmliZXMgYXMgZXF1aXZhbGVudCB0byBgcmFuayh0aWVzLm1ldGhvZCA9ICJtaW4iKWAuKioKCjUuIFdoYXQgZG9lcyBgMTozICsgMToxMGAgcmV0dXJuPyBXaHk/CgpgYGB7cn0KMTozICsgMToxMApgYGAKCioqVGhlIGFkZGl0aW9uIG9wZXJhdG9yIGlzIHZlY3RvcmlzZWQgd2l0aCByZWN5Y2xpbmcsIHNvIHRoZSBmaXJzdCAoMy1pdGVtKSB2ZWN0b3IgcmVwZWF0cyBpdHNlbGYgdGhyb3VnaCB1bnRpbCBpdCBtYXRjaGVzIHRoZSBzZWNvbmQgKDEwLWl0ZW0pIHZlY3RvciwgaS5lLiwgaXQgZW5kcyB1cCBiZWluZyBlcXVpdmFsZW50IHRvIGBjKDEsIDIsIDMsIDEsIDIsIDMsIDEsIDIsIDMsIDEpICsgYygxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5LCAxMClgLioqCgo2LiBXaGF0IHRyaWdvbm9tZXRyaWMgZnVuY3Rpb25zIGRvZXMgUiBwcm92aWRlPwoKKipTZWUgYD9UcmlnYCBmb3IgaW5mbyBvbiB0aGUgcGFja2FnZSB3aXRoIHRoZSBtYWluIHRyaWdvbm9tZXRyeSBmdW5jdGlvbnM6IGBjb3MoKWAsIGBzaW4oKWAsIGB0YW4oKWAsIGBhY29zKClgLCBgYXNpbigpYCwgYGF0YW4oKWAsIGBhdGFuMigpYCwgYGNvc3BpKClgLCBgc2lucGkoKWAgYW5kIGB0YW5waSgpYC4qKgoKIyA1LjYgR3JvdXBlZCBzdW1tYXJpZXMgd2l0aCBgc3VtbWFyaXNlKClgCgojIyA1LjYuNyBFeGVyY2lzZXMKCjEuIEJyYWluc3Rvcm0gYXQgbGVhc3QgNSBkaWZmZXJlbnQgd2F5cyB0byBhc3Nlc3MgdGhlIHR5cGljYWwgZGVsYXkgY2hhcmFjdGVyaXN0aWNzIG9mIGEgZ3JvdXAgb2YgZmxpZ2h0cy4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBzY2VuYXJpb3M6CgotIEEgZmxpZ2h0IGlzIDE1IG1pbnV0ZXMgZWFybHkgNTAlIG9mIHRoZSB0aW1lLCBhbmQgMTUgbWludXRlcyBsYXRlIDUwJSBvZiB0aGUgdGltZS4KCi0gQSBmbGlnaHQgaXMgYWx3YXlzIDEwIG1pbnV0ZXMgbGF0ZS4KCi0gQSBmbGlnaHQgaXMgMzAgbWludXRlcyBlYXJseSA1MCUgb2YgdGhlIHRpbWUsIGFuZCAzMCBtaW51dGVzIGxhdGUgNTAlIG9mIHRoZSB0aW1lLgoKLSA5OSUgb2YgdGhlIHRpbWUgYSBmbGlnaHQgaXMgb24gdGltZS4gMSUgb2YgdGhlIHRpbWUgaXTigJlzIDIgaG91cnMgbGF0ZS4KCldoaWNoIGlzIG1vcmUgaW1wb3J0YW50OiBhcnJpdmFsIGRlbGF5IG9yIGRlcGFydHVyZSBkZWxheT8KCjIuIENvbWUgdXAgd2l0aCBhbm90aGVyIGFwcHJvYWNoIHRoYXQgd2lsbCBnaXZlIHlvdSB0aGUgc2FtZSBvdXRwdXQgYXMgYG5vdF9jYW5jZWxsZWQgJT4lIGNvdW50KGRlc3QpYCBhbmQgYG5vdF9jYW5jZWxsZWQgJT4lIGNvdW50KHRhaWxudW0sIHd0ID0gZGlzdGFuY2UpYCAod2l0aG91dCB1c2luZyBjb3VudCgpKS4KCmBgYHtyfQpub3RfY2FuY2VsbGVkIDwtIGZsaWdodHMgJT4lIAogIGZpbHRlcighaXMubmEoZGVwX2RlbGF5KSwgIWlzLm5hKGFycl9kZWxheSkpCiMgRXF1aXZhbGVudCB0byBub3RfY2FuY2VsbGVkICU+JSBjb3VudChkZXN0KToKbm90X2NhbmNlbGxlZCAlPiUKICBncm91cF9ieShkZXN0KSAlPiUKICBzdW1tYXJpc2UobigpKQojIEVxdWl2YWxlbnQgdG8gbm90X2NhbmNlbGxlZCAlPiUgY291bnQodGFpbG51bSwgd3QgPSBkaXN0YW5jZSk6Cm5vdF9jYW5jZWxsZWQgJT4lCiAgZ3JvdXBfYnkodGFpbG51bSkgJT4lCiAgc3VtbWFyaXNlKG4gPSBzdW0oZGlzdGFuY2UpKQpgYGAKCjMuIE91ciBkZWZpbml0aW9uIG9mIGNhbmNlbGxlZCBmbGlnaHRzIGAoaXMubmEoZGVwX2RlbGF5KSB8IGlzLm5hKGFycl9kZWxheSkgKWAgaXMgc2xpZ2h0bHkgc3Vib3B0aW1hbC4gV2h5PyBXaGljaCBpcyB0aGUgbW9zdCBpbXBvcnRhbnQgY29sdW1uPwoKNC4gTG9vayBhdCB0aGUgbnVtYmVyIG9mIGNhbmNlbGxlZCBmbGlnaHRzIHBlciBkYXkuIElzIHRoZXJlIGEgcGF0dGVybj8gSXMgdGhlIHByb3BvcnRpb24gb2YgY2FuY2VsbGVkIGZsaWdodHMgcmVsYXRlZCB0byB0aGUgYXZlcmFnZSBkZWxheT8KCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JQogIHN1bW1hcmlzZSgKICAgIG5fY2FuY2VsbGVkID0gc3VtKGlzLm5hKGRlcF90aW1lKSkKICApICU+JQogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBuX2NhbmNlbGxlZCkpICsKICBnZW9tX2JhcigpCmBgYAoKKipNb3N0IGRheXMgaGF2ZSBhIHJlbGF0aXZlbHkgc21hbGwgbnVtYmVyIG9mIGZsaWdodHMgY2FuY2VsbGVkIGFuZCB0aGVyZSBhcmUgc29tZSBpbmZyZXF1ZW50IGRheXMgd2hlcmUgbWFueSBmbGlnaHRzIGFyZSBjYW5jYWxsZWQuKioKCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JQogIHN1bW1hcmlzZSgKICAgIHByb3BfY2FuY2VsbGVkID0gbWVhbihpcy5uYShkZXBfdGltZSkpLAogICAgbWVhbl9kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpCiAgKSAlPiUKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gbWVhbl9kZWxheSwgeSA9IHByb3BfY2FuY2VsbGVkKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQpgYGAKCioqVGhlcmUgYXJlIHNvbWUgdW51c3VhbCBkYXlzIGJ1dCB0aGUgZ2VuZXJhbCBwYXR0ZXJuIGlzIHRoYXQgZGF5cyB3aXRoIGxvbmdlciBkZWxheXMgYWxzbyB0ZW5kIHRvIGhhdmUgYSBncmVhdGVyIHByb3BvcnRpb24gb2YgZmxpZ2h0cyBjYW5jZWxsZWQuKioKCjUuIFdoaWNoIGNhcnJpZXIgaGFzIHRoZSB3b3JzdCBkZWxheXM/IENoYWxsZW5nZTogY2FuIHlvdSBkaXNlbnRhbmdsZSB0aGUgZWZmZWN0cyBvZiBiYWQgYWlycG9ydHMgdnMuIGJhZCBjYXJyaWVycz8gV2h5L3doeSBub3Q/IChIaW50OiB0aGluayBhYm91dCBgZmxpZ2h0cyAlPiUgZ3JvdXBfYnkoY2FycmllciwgZGVzdCkgJT4lIHN1bW1hcmlzZShuKCkpYCkKCmBgYHtyfQphaXJsaW5lX2RlbGF5cyA8LSBmbGlnaHRzICU+JQogIGdyb3VwX2J5KGNhcnJpZXIpICU+JQogIHN1bW1hcmlzZSgKICAgIG1lYW5fZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSwKICAgIG5fZmxpZ2h0cyA9IG4oKQogICkgJT4lCiAgYXJyYW5nZShkZXNjKG1lYW5fZGVsYXkpKQphaXJsaW5lX2RlbGF5cwpgYGAKCioqRnJvbnRpZXIgKEY5KSBhbmQgQWlyVHJhbiAoRkwpIGFyZSB0aGUgdHdvIHdpdGggdGhlIGhpZ2hlc3QgbWVhbiBhcnJpdmFsIGRlbGF5cyBidXQgbm90ZSB0aGF0IHRoZXkgaGF2ZSBhIHJlbGF0aXZlbHkgbG93IG51bWJlciBvZiBmbGlnaHRzIChpbiB0aGUgaHVuZHJlZHMgYW5kIHRob3VzYW5kcywgcmVzcGVjdGl2ZWx5KS4gT2YgYWlybGluZXMgdGhhdCBoYXZlIHRlbnMgb2YgdGhvdXNhbmRzIG9mIGZsaWdodHMsIEV4cHJlc3NKZXQgKEVWKSBoYXMgbWVhbiBkZWxheXMgNSBtaW51dGVzIGxvbmdlciB0aGFuIG90aGVyIGFpcmxpbmVzLiAoTm90ZSB0aGF0IGNhcnJpZXIgY29kZXMgY2FuIGJlIG1hdGNoZWQgdG8gYWlybGluZSBuYW1lcyBpbiB0aGUgYGFpcmxpbmVzYCBkYXRhIGZpbGUuKSoqCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBhaXJsaW5lX2RlbGF5cykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gbl9mbGlnaHRzLCB5ID0gbWVhbl9kZWxheSkpCmBgYAoKYGBge3J9CmFpcmxpbmVfZGVzdF9kZWxheXMgPC0gZmxpZ2h0cyAlPiUKICBncm91cF9ieShjYXJyaWVyLCBkZXN0KSAlPiUKICBzdW1tYXJpc2UoCiAgICBtZWFuX2RlbGF5ID0gbWVhbihhcnJfZGVsYXksIG5hLnJtID0gVFJVRSksCiAgICBuX2ZsaWdodHMgPSBuKCkKICApICU+JQogIGFycmFuZ2UoZGVzYyhtZWFuX2RlbGF5KSkKYWlybGluZV9kZXN0X2RlbGF5cwpgYGAKCioqSW4gYXR0ZW1wdGluZyB0byBkaXNlbnRhbmdsZSBjYXJyaWVyIHZzIGFpcnBvcnQgaXNzdWVzLCBzb21lIG9mIHRoZSBjb21iaW5hdGlvbnMgb2YgY2FycmllciBhbmQgYWlycG9ydCBoYXZlIGEgdmVyeSBzbWFsbCBudW1iZXIgb2YgZmxpZ2h0cyBhbmQgc2hvdWxkIGJlIGludGVycHJldGVkIHdpdGggY2F1dGlvbi4qKgoKYGBge3J9CmdncGxvdChkYXRhID0gYWlybGluZV9kZXN0X2RlbGF5cykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gbl9mbGlnaHRzLCB5ID0gbWVhbl9kZWxheSksIGFscGhhID0gMi8xMCkKYGBgCgoqKkkgbWlnaHQgY29tZSBiYWNrIGFuZCBleHBsb3JlIHRoZSBkYXRhIHNvbWUgbW9yZSB0byBzZWUgd2hhdCBjb25jbHVzaW9ucyBjYW4gYmUgZHJhd24gd2l0aCB0aGUgdm9sYXRpbGUgc21hbGwgc3Vic2FtcGxlcyByZW1vdmVkLioqCgo2LiBGb3IgZWFjaCBwbGFuZSwgY291bnQgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJlZm9yZSB0aGUgZmlyc3QgZGVsYXkgb2YgZ3JlYXRlciB0aGFuIDEgaG91ci4KCjcuIFdoYXQgZG9lcyB0aGUgc29ydCBhcmd1bWVudCB0byBgY291bnQoKWAgZG8uIFdoZW4gbWlnaHQgeW91IHVzZSBpdD8KCioqSWYgYHNvcnQgPSBUUlVFYCB0aGVuIHRoZSBgY291bnQoKWAgZnVuY3Rpb24gYXJyYW5nZXMgaXRzIG91dHB1dCBpbiBkZXNjZW5kaW5nIG9yZGVyLiBJIHdvdWxkIHVzZSBpdCB3aGVuIHlvdSB3YW50IHRvIGZpbmQgdGhlIG1vc3QgZnJlcXVlbnQgdmFsdWVzLCBhcyBpdCBzYXZlcyBwaXBpbmcgdGhlIGBjb3VudCgpYCByZXN1bHRzIGludG8gYW4gYGFycmFuZ2UoKWAgY2FsbC4qKgoKYGBge3J9Cm5vdF9jYW5jZWxsZWQgJT4lIGNvdW50KGRlc3QsIHNvcnQgPSBUUlVFKQpgYGAK